package com.intretech.umsin.oss.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.intretech.umsin.oss.entity.FileType;
import com.intretech.umsin.oss.param.FileInfoParam;
import com.intretech.umsin.oss.param.UploadParam;
import com.intretech.umsin.oss.service.FileService;
import com.intretech.umsin.oss.service.MetadataStoreService;
import com.intretech.umsin.oss.util.FileUtils;
import com.intretech.umsin.oss.vo.FileInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

/**
 * @version V1.0
 * @ClassName: AbstractFileService
 * @Description: AbstractFileService 文件服务抽象类,根据filetype 实例化具体oss对象
 * @author: 李启联
 * @date: 2022-04-24 14:20
 * @Copyright: Copyright (c) 2022
 */
@Slf4j
public abstract class  AbstractFileService implements FileService {

    //工作目录下开放的公共目录名称,外部可随意访问
    private static final String PUBLIC_WORK_DIR = "public";

    //工作目录下隐私的目录名称,外部不可随意访问
    private static final String PRIVATE_WORK_DIR = "private";

    /**
     * OSS存储类型
     * @return
     */
    @Override
    public abstract FileType fileType();

    /**
     * 文件元数据存储服务
     * @return
     */
    protected abstract MetadataStoreService getMetadataStoreService();

    /**
     * 上传文件
     *
     * @param file
     * @param fileInfo
     */
    protected abstract void uploadFile(MultipartFile file, FileInfoVo fileInfo) throws Exception;

    /**
     * 删除文件资源
     *
     * @param fileInfo
     * @return
     */

    protected abstract boolean deleteFile(FileInfoVo fileInfo);


    /**
     * 对查询返回的文件列表设置其对应文件访问的URL
     * @param fileInfo
     */
    protected abstract void setFileUrl(FileInfoVo fileInfo);


    protected abstract byte[] downloadFile(FileInfoVo fileInfo) throws Exception;

    /**
     * 下载文件
     *
     * @param fileInfo
     * @return
     */
    @Override
    public byte[] download(FileInfoVo fileInfo) throws Exception {
//        FileInfoVo file = getMetadataStoreService().findById(fileInfo.getId());
//        if(ObjectUtils.isEmpty(file)){
//            throw new Exception("找不到对应的附件信息!");
//        }
        return downloadFile(fileInfo);
    }

    /**
     * 上传文件操作方法
     *
     * @param file   待上传的文件
     * @param params 附加参数 如({path:'A/B';businessId:'000001'} path 服务端的存放路径(如：FTP服务端存储的分类路径,即为文件的路径前缀) ;bizId 业务ID)
     * @return 文件信息
     * @throws Exception
     */
    @Override
    public FileInfoVo upload(MultipartFile file, UploadParam params) throws Exception {
        String path ="";
        FileInfoVo fileInfo = FileUtils.getFileInfo(file);
        Boolean ifPublic = ObjectUtils.isEmpty(params.getIfPublic()) ? true : params.getIfPublic();
        path = ObjectUtils.isEmpty(params.getPath()) ? "" : params.getPath();
        //设置路径前缀
        if(!ifPublic){
            path = "/"+PRIVATE_WORK_DIR+ (path.length() > 0 ? "/"+path : "");
        }else{
            path = "/"+PUBLIC_WORK_DIR+ (path.length() > 0 ? "/"+path : "");
        }
        fileInfo.setFilePathPrefix(path);
        //设置业务ID
        if(StringUtils.isNotBlank(params.getBusinessId())){ fileInfo.setBusinessId(params.getBusinessId()); }
        //设置用户自定义字段
        if(StringUtils.isNotBlank(params.getUserDefine())){ fileInfo.setUserDefine(params.getUserDefine()); }
        //设置OSS存储类型
        fileInfo.setSource(fileType().toString());
        //如果业务ID不为空，需要把上传文件生成的MD5字符串并上业务ID去重新算出MD5值作为MD5
        if(StringUtils.isNotBlank(fileInfo.getBusinessId())){
            fileInfo.setFileMd5(FileUtils.stringMd5(fileInfo.getFileMd5()+fileInfo.getBusinessId().trim()));
        }

        //判断同一业务ID下是否存在同一文件,如果存在直接返回
        FileInfoVo fileInfoExist = getMetadataStoreService().findByFileMd5(fileInfo.getFileMd5());
        if(!ObjectUtils.isEmpty(fileInfoExist)){
            setFileUrl(fileInfoExist);
            return fileInfoExist;
        }

        uploadFile(file, fileInfo);

        fileInfo.setPath(fileInfo.getFilePathPrefix() + "/" + fileInfo.getTagsName());

        FileInfoVo savedFile = getMetadataStoreService().save(fileInfo);// 将文件信息保存到数据库

        setFileUrl(savedFile);

        return savedFile;
    }

    /**
     * 删除文件
     *
     * @param fileInfo
     */
    @Override
    public void delete(FileInfoVo fileInfo) {
        deleteFile(fileInfo);
        getMetadataStoreService().delete(String.valueOf(fileInfo.getId()));
        log.info("删除文件：{}", fileInfo);
    }



    /**
     * 根据ID找到文件信息
     *
     * @param id 文件对应的md5值作为该文件的ID标识
     * @return
     */
    @Override
    public FileInfoVo getById(String id) {
        FileInfoVo fileInfo = getMetadataStoreService().findById(id);
        setFileUrl(fileInfo);
        return fileInfo;
    }


    /**
     * 根据查询条件查询文件元信息
     *
     * @param param    查询条件参数
     * @param pageNo   当前页码
     * @param pageSize 每页条数
     * @return 自定义分页对象
     */
    @Override
    public IPage<FileInfoVo> findByPageResult(FileInfoParam param, Integer pageNo, Integer pageSize) {
        IPage<FileInfoVo> pageResult = getMetadataStoreService().findByPageResult(param, pageNo, pageSize);
        pageResult.getRecords().forEach(r->setFileUrl(r));
        return pageResult;
    }

    /**
     * 据业务ID条件得到文件列表
     *
     * @param busId 业务ID
     * @return
     */
    @Override
    public List<FileInfoVo> findFilesByBusId(String busId) {
        List<FileInfoVo> ls = getMetadataStoreService().findByBusId(busId);
        if(CollectionUtils.isNotEmpty(ls)) {
            ls.forEach(r -> setFileUrl(r));
        }
        return ls;
    }

    /**
     * 删除业务单下的所有文件
     *
     * @param busId
     */
    @Override
    public void deleteByBusId(String busId) {
        List<FileInfoVo> ls = getMetadataStoreService().findByBusId(busId);
        if(CollectionUtils.isNotEmpty(ls)){
            ls.forEach(f->delete(f));
        }
    }
}
